Skip to content

feat(acp): bring your own harness (BYOH) — generic ACP runtime seam + settings gallery#2773

Draft
wpfleger96 wants to merge 14 commits into
mainfrom
duncan/byoh-generic-acp-harness
Draft

feat(acp): bring your own harness (BYOH) — generic ACP runtime seam + settings gallery#2773
wpfleger96 wants to merge 14 commits into
mainfrom
duncan/byoh-generic-acp-harness

Conversation

@wpfleger96

@wpfleger96 wpfleger96 commented Jul 24, 2026

Copy link
Copy Markdown
Member

What

Implements a "bring your own harness" (BYOH) generic ACP mechanism — replacing per-harness backend code with a data-driven 3-tier system:

  • Tier 1 (compiled-in builtins): goose, claude, codex, buzz-agent — unchanged behavior
  • Tier 2 (bundled presets): cursor, omp, grok, opencode, kimi, amp, hermes, openclaw, and any future additions — defined in PRESET_HARNESSES, no code duplication, icons stay TerminalSquare/bundled-asset-only
  • Tier 3 (user-defined custom): JSON definitions saved to custom_harnesses/ under app data; managed via Settings → Agents UI

Changes

Core data model

  • HarnessDefinition — id, label, command, args, env, install URL/hint
  • PRESET_HARNESSES static table — single source of truth for all presets; preset_harness_ids() derives reserved IDs (D-11: no hand-maintained copy)
  • source: "builtin" | "preset" | "custom" tagging on every catalog entry

Persistence (B-4, B-6)

  • save_custom_harness_to_dir(dir, definition, rename_old_id) — backup-swap atomic write (backs up target → .bak, commits temp → target, restores .bak on failure, removes .bak on success); safe on Windows where fs::rename over an existing file is "access denied"
  • save_and_warm / delete_and_warm — hold PERSIST_MUTEX for the write + registry-warm pair, eliminating the lost-update race (B-6) where two concurrent saves could interleave their warm calls and leave a stale registry snapshot
  • Validate-before-mutate: both IDs and env validated before any filesystem mutation

Env validation boundary (B-3)

  • validate_harness_definition_pub calls validate_user_env_keys on definition env at save AND load
  • Rejects malformed keys (BUZZ_AUTH_TAG=x forgery shape), reserved keys (BUZZ_MANAGED_AGENT etc.), NUL bytes, oversized values

TypeScript boundary (B-2 / Thufir CRITICAL)

  • RawAcpRuntimeCatalogEntry now declares definition_env?: Record<string,string> and source: "builtin" | "preset" | "custom"
  • fromRawAcpRuntimeCatalogEntry maps definition_env → definitionEnv (camelCase); absent field defaults to {}
  • Edit form reads entry.definitionEnv — env no longer erased on save-then-edit cycle

Unified descriptor (Phase A / Thufir F4)

  • EffectiveHarnessDescriptor { command, args, env } in readiness.rs
  • resolve_effective_harness_descriptor() — single resolver used by spawn, spawn_hash, summary, get_agent_models (both saved and unsaved), and readiness
  • No competing arg-resolution forms

Other fixes

  • B-5: stop freezing runtime.defaultArgs into record.agent_args on normal create paths
  • B-7: readiness exec-check — MissingBinary variant for custom commands not found on PATH
  • B-8: onboarding transition — setTimeout(0) removed, parent-owned route intent via navigateAfterComplete prop
  • C-9: collector-discriminating sweep tests with injectable filters
  • C-10: HarnessManagementCard uses harnessGalleryLogic helpers (killed duplicate filter/sort)
  • D-11: BUILTIN_IDS derived from PRESET_HARNESSES (no hand-maintained copy)
  • D-12: mobile/pubspec.lock churn reverted
  • D-13: false ownership fast-path comment fixed
  • D-14: URL scheme validation for installInstructionsUrl
  • D-15: OpenClaw Gateway env-locus README line

Tests added

B-4 persistence (6 tests): save_to_dir_create_writes_file_and_loads_back, save_to_dir_same_id_edit_replaces_content, save_to_dir_backup_is_cleaned_up_after_same_id_edit, save_to_dir_rename_removes_old_file_and_creates_new, save_to_dir_rename_nonexistent_old_id_is_non_fatal, save_to_dir_roundtrip_with_env_preserves_values

B-3 env validation (6 tests): validate_rejects_malformed_key_with_equals_sign, validate_rejects_reserved_key_buzz_managed_agent, validate_rejects_reserved_key_case_insensitive, validate_rejects_nul_byte_in_value, validate_rejects_value_over_per_value_size_limit, validate_accepts_well_formed_env

B-2 API boundary (4 TS tests in tauri.test.mjs): fromRawAcpRuntimeCatalogEntry maps definition_env to definitionEnv, defaults definitionEnv to {} when absent, preserves source preset, env round-trips through edit payload shape

Preset catalog

ID Label Command
cursor Cursor cursor-agent acp
omp Oh My Pi omp acp
grok Grok Build grok agent --always-approve stdio
opencode OpenCode opencode acp
kimi Kimi Code kimi acp
amp Amp amp-acp
hermes Hermes Agent hermes-acp
openclaw OpenClaw openclaw acp

Review-fix pass (2026-07-26, Eva)

Fixes from the three-way review (Wren / Dawn / Eva) in the buzz-generic-acp-harnesses thread, pushed as new commits (no rewrite):

  1. installHint edit round-trip — form seeding extracted to formValuesFromCatalogEntry (single source of truth), input rendered, full-definition lossless round-trip regression.
  2. Dangling-delete coherence — delete allowed; confirm counts referencing agents (direct pin + persona-inherited); summary rows render harness (deleted): <id>; spawn errors become actionable sentences (user_facing_harness_error); composed delete→summary→start test.
  3. Comma-in-args — rejected at validate_harness_definition (shared by save AND disk load), mirrored inline in the form.
  4. Registry publish race — collision/dup filtering moved into load_custom_harnesses (both loaders inherit shadowing rules); discovery publishes by re-reading the dir under persist_mutex (lock scoped to publish only); deterministic interleaving regressions for save-during-discovery and delete-during-discovery.
  5. Mechanical — discarded belongs_to_us sweep arg deleted, load_global_agent_config hoisted out of the per-record summary loop, duplicated doc paragraph + stray SAFETY comment removed.
  6. PGID test de-flaked — leader kept alive through the assertion.

Known follow-up (filed in review, not blocking): file-size split-outs queued in check-file-sizes.mjs entries.

Gate table — head bf53f1d60

Gate Result
cargo test --lib (desktop/src-tauri) 1701 passed, 0 failed, 14 ignored
desktop JS suite (pnpm test) 3605 passed, 0 failed
tsc --noEmit clean
biome check + file-size/px/pubkey checks clean
cargo clippy --lib -- -D warnings clean
cargo fmt --check clean

PR head: bf53f1d60e3cbd07392e1287b83bb37ba90d0d33 — includes merge of origin/main (c2a4ee711, conflicts in agent_models composed with #2890's live Databricks discovery)

@wpfleger96
wpfleger96 requested a review from a team as a code owner July 24, 2026 22:01
@wpfleger96
wpfleger96 marked this pull request as draft July 24, 2026 23:16
@wpfleger96
wpfleger96 force-pushed the duncan/byoh-generic-acp-harness branch 2 times, most recently from bda1871 to fa4034b Compare July 25, 2026 02:55
@wpfleger96
wpfleger96 force-pushed the duncan/byoh-generic-acp-harness branch from fa4034b to 5ebabda Compare July 25, 2026 05:16
Implement a generic BYOH mechanism that lets any ACP-speaking harness
(Cursor, Pi, Amp, OpenClaw, Hermes, etc.) work with Buzz without
maintaining separate backend code per harness type.

Backend (Rust):
- Add HarnessDefinition, HarnessSource, PRESET_HARNESSES (8 presets:
  cursor/omp/grok/opencode/kimi/amp/hermes/openclaw) + custom harness
  loading from ~/.config/buzz/custom-harnesses/
- warm_harness_registry_from_dir called at startup (before restore) and
  transactionally on save/delete; try_record_agent_command returns typed
  DANGLING_HARNESS_ID:<id> error — never silently falls to default
- Full descriptor resolved at spawn/readiness/spawn_hash paths; definition
  env merges below Buzz-reserved vars; edit round-trip carries definition_env
  in catalog entry
- All sweep paths (reap_dead_instance_agents, kill_stale_tracked_processes,
  receipt cleanup) use marker/receipt ownership — no name-gating
- save_custom_harness validates before mutation; atomic write + old-file
  delete on rename; preset ids reserved in check_id_collision
- buzz_sweep_owns_process cross-platform (no #[cfg(unix)] guard)

Frontend (TypeScript):
- Settings > Agents BYOH gallery: preset cards (Detected/Not-installed +
  docs link, no Add button), custom cards (full edit/delete)
- ArgsEditor (repeatable rows) + EnvEditor (KEY=VALUE pairs)
- HarnessManagementCard + harnessFormLogic.ts (21 behavior tests)
- harnessGalleryLogic.ts (11 tests: detected-first sort, preset filtering)
- Onboarding: actionable 'More harnesses...' button → Settings > Agents
- PRESET_LOGOS bundled map; avatarUrl stripped from all surfaces

Tests: +27 frontend / +20 Rust lib (registry lifecycle, env round-trip,
sweep ownership, legacy-JSON serde, preset collision)

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
@wpfleger96
wpfleger96 force-pushed the duncan/byoh-generic-acp-harness branch from 5ebabda to 277d8b0 Compare July 25, 2026 05:20
npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 and others added 3 commits July 25, 2026 01:36
…safe registry

B-4 (persistence contract): Replace the two simulated round-trip tests that
called raw fs::write/remove_file with six tests that exercise the real
save_custom_harness_to_dir helper directly:
- save_to_dir_create_writes_file_and_loads_back
- save_to_dir_same_id_edit_replaces_content
- save_to_dir_backup_is_cleaned_up_after_same_id_edit
- save_to_dir_rename_removes_old_file_and_creates_new
- save_to_dir_rename_nonexistent_old_id_is_non_fatal
- save_to_dir_roundtrip_with_env_preserves_values

B-3 (env validation boundary): Six tests exercising validate_harness_definition_pub
integration with validate_user_env_keys for the documented attack surfaces:
- validate_rejects_malformed_key_with_equals_sign (BUZZ_AUTH_TAG=x forgery shape)
- validate_rejects_reserved_key_buzz_managed_agent (ownership marker)
- validate_rejects_reserved_key_case_insensitive (lowercase bypass)
- validate_rejects_nul_byte_in_value (Command::env panic protection)
- validate_rejects_value_over_per_value_size_limit
- validate_accepts_well_formed_env

B-2 (API boundary): Export fromRawAcpRuntimeCatalogEntry from tauri.ts and
add four tests to tauri.test.mjs proving the Rust definition_env snake_case
field is mapped to definitionEnv camelCase, that absent definition_env defaults
to {} (not undefined), and that env round-trips end-to-end through the mapper
so a save-then-edit cycle cannot erase definition env.

B-6 (concurrent-safe registry): Add save_and_warm + delete_and_warm functions
in custom_harnesses.rs that hold a PERSIST_MUTEX for the filesystem mutation
and the subsequent warm_harness_registry_from_dir call as an atomic unit.
Update save_custom_harness and delete_custom_harness Tauri commands to use
these helpers. Eliminates the lost-update window where two concurrent saves
could interleave their warm calls and produce a stale registry snapshot.

File-size override added for custom_harnesses.rs (1042 lines after the new
tests; queued to split once the feature stabilizes).

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
D-12: Revert mobile/pubspec.lock to origin/main.  The meta 1.17→1.18 /
test 1.30→1.31 churn was a no-source-change lockfile bump introduced by
accident; reverting eliminates the unrelated diff.

D-15: Add OpenClaw execution-locus note to the preset's install_hint.
Eva's finding: openclaw acp executes tools inside the Gateway daemon, not
in the Desktop process.  Desktop-injected BUZZ_* env vars reach the
openclaw harness process itself but do NOT automatically propagate to
the Gateway's execution environment.  The install_hint now surfaces this
caveat so users who need BUZZ_* credentials at execution time know they
must set them on the Gateway's own environment.

F8 (onboarding navigate test): New pure-logic test file
postOnboardingNav.test.mjs proves the App.tsx postOnboardingNav
useEffect predicate — navigateAfterComplete does not fire before
machine.stage reaches 'ready', fires exactly once on the ready
transition, is cleared after firing (no double-fire), fires immediately
if nav arrives while already ready, and carries the exact
{to: '/settings', search: {section: 'agents'}} shape from
MachineOnboardingFlow's navigateToAgentSettings action.

C-10 (e2eBridge + handler tests): Extract save_custom_harness /
delete_custom_harness handler logic into e2eBridgeCustomHarnesses.ts
(exported module-level functions + mockCustomHarnesses Map +
resetMockCustomHarnesses).  Wire the handlers into e2eBridge.ts:
  - mockCustomHarnesses imported and appended to discover_acp_providers
    results so saved custom harnesses appear in future discovery calls
  - case 'save_custom_harness' → handleSaveCustomHarness
  - case 'delete_custom_harness' → handleDeleteCustomHarness
New test file e2eBridgeCustomHarnesses.test.mjs (14 tests across 3
describe groups) proves: save → store, non-empty env preserved,
empty env absent, same-ID edit replaces, rename removes old + inserts
new, delete removes, delete idempotent, Map reference is shared.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
… delete-error knob

C-10 (full form — Playwright spec):
  Add desktop/tests/e2e/harness-management.spec.ts with 10 tests covering:
    - Preset gallery: detected badge present for available preset
    - Preset gallery: install link shown, no badge for not-installed preset
    - Add custom harness: form save → row appears in list
    - Edit preserves env vars: definitionEnv round-trip via edit form
    - Same-ID edit replaces row without duplication
    - Rename: old row gone, new row present
    - Delete success: row disappears
    - Delete failure: inline error shown, row kept (error-injection knob)
    - PATH badge on custom row: Detected badge when availability === 'available'
    - F8 onboarding navigate: setup-page More-harnesses click →
      completes onboarding → Settings → Agents (settings-harness-management)
      This exercises the real App.tsx effect that gates router.navigate()
      on machine.stage === 'ready', which postOnboardingNav.test.mjs cannot
      cover (it simulates the predicate, not the render path).

Delete-error injection knob:
  - Add deleteCustomHarnessError?: string to MockBridgeOptions in bridge.ts
  - Add same field to MockE2eConfig in e2eBridge.ts
  - Thread config through to handleDeleteCustomHarness so the e2e bridge
    throws when the knob is set (mirrors acpAuthMethodsError pattern)
  - Add unit test: throws when knob set + entry survives in store

D-15 (README execution-locus sentence, full placement):
  The install_hint placement (already in discovery.rs) is correct and stays.
  Add the missing sentence to the README blockquote at line 265 per the brief:
  Desktop-injected BUZZ_* env vars do NOT reach the execution locus unless
  set on the Gateway's own environment.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
wpfleger96 pushed a commit that referenced this pull request Jul 25, 2026
@wpfleger96

Copy link
Copy Markdown
Member Author

Screenshots — BYOH Generic ACP Harness (head 5862b3109)

Preset gallery — Hermes (detected) + OpenClaw (not installed)

Preset cards rendered from the backend PRESET_HARNESSES source of truth; Hermes shows the green Detected badge, OpenClaw shows the Install link.
01-harness-preset-gallery

Hermes preset card — detected badge state

Green "Detected" badge appears when hermes-acp is found on PATH.
02-harness-preset-hermes-detected

OpenClaw preset card — not installed state

"Install" link surfaced for not-installed presets; routes to the configured install_instructions_url.
03-harness-preset-openclaw-not-installed

Full Bring-Your-Own-Harness section

Complete section with header, preset gallery, and "Add custom harness" entry point.
04-harness-management-full-section

Add custom harness form — env rows

Name → ID auto-derive, Command field with live PATH badge, Env vars row with KEY=value inputs.
05-add-custom-harness-form-with-env

Edit harness form — env preserved on re-open

Edit mode shows pre-populated env rows; env round-trips correctly through the definitionEnv boundary mapping.
06-edit-custom-harness-env-preserved

Custom harness list row

Saved custom harness in the list with availability status badge and Edit/Delete controls.
07-custom-harness-list

PRESET_HARNESSES ships 8 tier-2 presets; PRESET_LOGOS and
desktop/public/harness-logos/ covered only 6. `hermes` and `openclaw`
fell through to the generic TerminalSquare glyph beside six siblings
showing real brand marks, and RuntimeIcon's onError fallback made a
missing file indistinguishable from an unmapped id at runtime.

Add both marks from their MIT-licensed upstreams:

- hermes.png from NousResearch/hermes-agent@6ad632b
  website/static/img/logo.png — cropped the baked-in border frame
  (a dark box once downscaled), padded square, 64x64, 16-colour
  palette. 1.7 KB, in line with the existing 1.0-3.4 KB siblings.
- openclaw.svg from openclaw/openclaw@b06f40a ui/public/favicon.svg —
  upstream animates the mascot with SMIL specifically so it plays
  inside <img>-loaded SVGs, which is exactly how RuntimeIcon renders
  it. Stripped the animation elements to hold the rest pose (verified
  pixel-identical to the upstream t=0 frame at 256px) so a settings
  list doesn't bob and blink.

Provenance and modifications are recorded in a new CREDITS.md next to
the assets, and the contributor guide's step 4 now requires that row.

The guard: the two sides of this mapping are a Rust slice and a TS
record, so nothing typechecks them against each other — which is why
the gap went unnoticed through review and screenshots. presetLogos.test.mjs
parses PRESET_HARNESSES out of discovery.rs and asserts every preset id
has a PRESET_LOGOS entry whose file exists on disk, plus the reverse
direction for stale entries. It follows the cross-language fixture
precedent in effortTable.fixture.test.mjs. Verified failing on each of
the three drift modes it claims to catch, not just passing green.

Importing RuntimeIcon.tsx from a test needed the node test loader to
tolerate Vite asset imports (`./claude.png?inline`), which it resolved
as a bare package and threw on; assets now load as an inert stub, the
same treatment CSS already gets.

Co-authored-by: tlongwell-block <109685178+tlongwell-block@users.noreply.github.com>
Signed-off-by: tlongwell-block <109685178+tlongwell-block@users.noreply.github.com>
Brings the branch up to date with origin/main (2a051a4) so CI and local
gates run against the tree that will actually merge. Requested as a merge
commit rather than a rebase to keep Duncan's authored history intact.

Two ratchet entries in desktop/scripts/check-file-sizes.mjs needed
composing, since both sides raised per-file ceilings from a shared base:

- src-tauri/src/commands/agent_discovery.rs — textual conflict. BYOH took
  1826 -> 2038 (+212); main #2767 took 1826 -> 1836 (+10). Resolved to
  2048 with both comment blocks kept, preserving the slack each side
  intended rather than discarding one growth.
- src-tauri/src/managed_agents/discovery.rs — no textual conflict, but the
  BYOH ceiling of 1715 predates main's +17 and the merged file is 1729
  lines, so the check failed on a clean auto-merge. Composed to 1732.

The overrides map is a ceiling (violation only when lineCount > limit), so
composing deltas is the resolution that matches the file's convention.

Verified on the merged tree: desktop file-size check clean, frontend suite
3554/3555 (the one failure is the pre-existing styled-qr-code 'Cannot find
package qrcode' local module-resolution fault, which fails identically on
pristine heads), presetLogos drift guard 10/10 with all 8 preset ids still
parsing out of main's modified discovery.rs, tsc --noEmit clean, biome
clean over 1640 files, just test-unit 5/5 crates, and desktop-tauri-test
1691/1691.

Co-authored-by: tlongwell-block <109685178+tlongwell-block@users.noreply.github.com>
Signed-off-by: tlongwell-block <109685178+tlongwell-block@users.noreply.github.com>
@OpenSpek

OpenSpek commented Jul 25, 2026

Copy link
Copy Markdown

Windows end-to-end proof completed against PR #2773 frozen at 0922869abe9f170879276588f3c4c51c292e230f.

Proof shape:

  • built and launched the disposable Windows desktop directly
  • launched installed hermes-acp.exe 0.19.0 directly (no wrapper)
  • one managed worker
  • openai-codex:gpt-5.6-sol
  • HERMES_ACP_SKIP_CONFIGURED_MCP=1
  • real channel mention routed through ACP
  • Hermes executed the bundled buzz CLI reply operation
  • Buzz displayed a thread reply attributed to the managed Hermes agent with the exact requested token

Two Windows Hermes terminal issues were exposed by the proof:

  1. the Git Bash startup probe could wedge after a timeout while an MSYS descendant retained captured pipe handles — already addressed by fix(acp_adapter): windows bash hang NousResearch/hermes-agent#69083;
  2. bash -l replaced the native host-injected PATH, hiding Buzz's bundled CLI from the terminal snapshot — addressed by fix(tools): preserve host PATH in Windows terminal snapshots NousResearch/hermes-agent#71433.

One Buzz-side safety finding: a blank/new managed-agent parallelism value inherited 24 on this machine and launched 24 workers. Explicitly setting 1 fixed containment. Please consider defaulting new managed ACP agents to 1, or requiring an explicit value/warning before launch.

This validates the generic ACP harness path on Windows; it does not imply a production routing cutover.

tlongwell-block and others added 2 commits July 25, 2026 11:57
The C-10 harness-management spec was 5 passed / 5 failed and had never run in
CI — it was added without an entry in either playwright.config.ts project's
testMatch, so no gate covered it. Three test-side defects, no product defects.

1. Ambiguous "Save" selector. getByRole("button", {name: "Save"}) substring-
   matches "Save defaults" from AgentDefaultsEditor, a sibling card in the
   same Settings -> Agents panel. Scoped to the form, exact match.

2. The mock catalog ignored its own mutation store. handleDiscoverAcpRuntimes
   returned a test-supplied acpRuntimesCatalog early, never merging
   mockCustomHarnesses — so save/delete mutated a store the seeded specs
   never read back, and add/edit/rename/delete all silently no-oped.

   Fixed with one mergeMockCustomHarnesses used by both catalog paths. A
   deleted *seeded* row has no store entry to remove, so removal needs a
   tombstone set rather than just the map; the same mechanism covers the id
   vacated by a rename.

3. The onboarding-nav test was unsatisfiable as written. It needs machine
   onboarding to run (community must not vouch for the active identity) and
   needsSetup to be false afterwards (or App.tsx renders WelcomeSetup instead
   of the router). skipCommunitySeed leaves zero communities and fails the
   second; the default seed stamps the active pubkey and fails the first.
   Seeding a community under a foreign pubkey satisfies both.

   Instrumenting this showed the navigation itself always worked — the hash
   became #/settings?section=agents on click, with WelcomeSetup painted over
   it because the router never mounted. The route intent is correct.

Adds unit coverage for the merge/tombstone logic; reverting the tombstone
filter fails exactly the delete and rename cases.

Verified at 91080e0: harness-management 10/10, unit suite 3560/3561 (the
one failure is the pre-existing styled-qr-code / missing qrcode package
fault, identical on a pristine tree), tsc clean, biome clean 1641 files,
check-file-sizes clean.

Co-authored-by: Dawn (sprout agent) <c6237ef84fa537c78dcee78efd2d4e59f728859c7f194da42ac51ededfa0be05@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: tlongwell-block <109685178+tlongwell-block@users.noreply.github.com>
Replace the incorrect Oh My Posh and Dify marks with the exact upstream Oh My Pi and Kimi Code assets. Use xAI’s official Grok logomark under its nominative brand terms, with presentation-only backgrounds for marks that need contrast.

Remove the unproven Cursor bitmap and deliberately retain the generic terminal fallback because Cursor publishes assets without granting redistribution permission. Record exact provenance and keep the drift guard explicit about that exception.

Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
@tlongwell-block

Copy link
Copy Markdown
Collaborator
2bae233534222b01139e44cb5cc9bc2ce2e778ee634fae11a72659916e97bd4c

npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67d and others added 6 commits July 25, 2026 20:28
Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
…lete coherence, registry races

Review fixes for the BYOH generic ACP harness PR (backend layer):

- Reject commas in harness args at validate_harness_definition, the
  shared boundary used by both save_custom_harness and the disk loader,
  so a definition that would corrupt the comma-joined agent_args wire
  format can neither be saved nor loaded.
- Dangling harness references (agent pins a deleted harness id) now
  resolve to a typed sentinel: summaries render 'harness (deleted): <id>'
  instead of silently falling back to buzz-agent, and spawn converts the
  sentinel to an actionable sentence via user_facing_harness_error.
- ManagedAgentSummary carries the record-level runtime pin so the UI can
  count referencing agents in the delete confirmation.
- Collision filtering moved into load_custom_harnesses so both loaders
  inherit preset-shadowing rules; discovery publishes the registry by
  re-reading the directory under persist_mutex (lock scoped to the
  publish only, never across auth probes), with deterministic
  interleaving tests for save-during-discovery and delete-during-discovery.
- load_global_agent_config hoisted out of build_managed_agent_summary
  into a parameter: one disk read per caller instead of per record.
- Cleanup: drop discarded sweep arg from buzz_sweep_owns_process, remove
  duplicated doc paragraph and stray SAFETY comment.
- De-flake grandchild PGID test: keep the process-group leader alive
  through the getpgid assertion with a trailing sleep.

Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
…a-arg inline mirror

Frontend layer of the review fixes:

- installHint survives edit: form seeding extracted to
  formValuesFromCatalogEntry (single source of truth for edit
  initialization — every user-editable field must appear there), input
  rendered, and a lossless entry -> form -> save-payload round-trip
  regression covers the full definition, not one field at a time.
- Delete confirmation counts referencing agents (direct record pins plus
  persona-inherited runtime) and names the blast radius: 'N agents use
  this harness and will stop launching.' Queries only run while the
  confirmation is open.
- Comma-in-args rejection mirrored inline in the form with an error
  naming the offending argument; the authoritative gate stays in
  validate_harness_definition on the Rust side.
- ManagedAgent (TS) carries the record-level runtime pin; e2e bridge
  mirrors the native wire shape (key always present, null when unpinned).

Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
Conflicts resolved in agent_models.rs / agent_models_tests.rs: main's #2890
(live Databricks discovery via effective_discovery_provider +
provider_env_var) composed with this branch's unified
resolve_effective_harness_descriptor. get_agent_models now passes the
record's saved provider plus the runtime's provider_env_var (from
known_acp_runtime on the descriptor command) into
effective_discovery_provider, replacing both main's
SavedAgentModelDiscoveryConfig snapshot and this branch's ad-hoc
BUZZ_AGENT_PROVIDER env read.

Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
check-file-sizes ceilings composed with the review-fix growth (each entry
documents the delta and why it is load-bearing): agents.rs +47 (config
hoist + dangling-summary tests), custom_harnesses.rs +153 (shared
collision filter + mutex-scoped publish seam + comma validation + tests),
discovery.rs +13 (publish-under-mutex), discovery/tests.rs +90
(interleaving regressions), types.ts +6 (runtime pin field). Also one
biome optional-chain warning in the new form-logic test.

Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
…outing (#2773)

Dawn's blocker: the loader-boundary collision/dedup enforcement was
untested — deleting either block left the full suite green. The stale
load_applies_id_collision_check only called check_id_collision directly
and its comment described the pre-fix architecture. Replaced with a
version that drives the REAL load_custom_harnesses against a shadowing
goose.json, plus a dedup twin (two files, same id -> len()==1). Both
verified to kill the mutants locally.

Wren's test-quality gap: the interleaving regressions pinned the
warm_harness_registry_locked seam but never executed discovery — a
stale-publish mutant of discover_acp_runtimes_from stayed green. Added
a cfg(test) pre_publish_test_hook between discovery's scan and its
registry publish, and two production-path regressions that land a
save/delete through the real discover_acp_runtimes_from call. Verified
both red under the stale-publish mutant.

Wren's product gap: the delete confirmation could be confirmed before
the blast-radius queries settled (pending queries rendered as zero
dependents with an enabled destructive button). New deleteConfirmState
drives the confirm UI: disabled + 'Checking which agents…' while
either query is initial-loading; on query failure deletion stays
possible but the copy says the count is unknown instead of claiming
zero. Unit-tested for pending/error/settled states.

Dawn + Wren both: get_agent_models now routes harness-resolution
errors through user_facing_harness_error (model_discovery_error
helper), so a dangling harness renders as a sentence, never the raw
DANGLING_HARNESS_ID sentinel. Also added Dawn's requested comment at
discovery's publish noting which tests pin the seam.

File-size ratchets bumped with documented deltas (all test/test-seam
growth).

Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants